#include "json.h"

#include <math.h>

namespace jlib {
namespace json {

	inline void __skip (const char *&p) {
		for (; *p == ' ' || *p == '\n' || *p == '\t'; ++p);
	}

	const char *parse (const char *s, Proxy &proxy) noexcept {
		char c = *s;
		switch (c) {
			case '"': {
				for (const char *p = ++s; ; ++p) {
					if (__builtin_expect(*p == '"', 0)) {
						if (proxy.onString(s, p))
							return nullptr;
						return ++p;
					}
					if (*p == '\\')
						++p;
					if (__builtin_expect(*p == '\0', 0)) {
						proxy.onError(p);
						return nullptr;
					}
				}
			}
			case '-':
			case '0'...'9': {
				do {
					long sign = 1;
					if (c == '-') {
						sign = -1;
						++s;
					}
					if ('0' <= *s && *s <= '9') {
						long integer = *s++ - '0';
						if (integer) {
							for (; '0' <= *s && *s <= '9'; ++s) {
								integer *= 10;
								integer += *s - '0';
							}
						}
						if (*s != '.' && *s != 'E' && *s != 'e') {
							if (proxy.onInteger(sign * integer))
								return nullptr;
							return s;
						}
						double decimal = integer;
						if (*s == '.') {
							++s;
							if ('0' <= *s && *s <= '9') {
								double accuracy = 0.1;
								decimal += (*s - '0') * accuracy;
								for (++s; '0' <= *s && *s <= '9'; ++s) {
									accuracy *= 0.1;
									decimal += (*s - '0') * accuracy;
								}
							}
							else break;
						}
						if (*s == 'E' || *s == 'e') {
							++s;
							bool reciprocal = false;
							if (*s == '+')
								++s;
							else if (*s == '-') {
								reciprocal = true;
								++s;
							}
							if ('0' <= *s && *s <= '9') {
								unsigned long exp = *s++ - '0';
								if (exp) {
									for (; '0' <= *s && *s <= '9'; ++s) {
										exp *= 10;
										exp += *s - '0';
									}
								}
								double base = decimal;
								decimal = 1.0;
								for (; exp; exp >>= 1, base *= base)
									if (exp & 1)
										decimal *= base;
								if (reciprocal)
									decimal = 1.0 / decimal;
							}
							else break;
						}
						if (proxy.onFloat(sign * decimal))
							return nullptr;
						return s;
					}
				}
				while (false);
				proxy.onError(s);
				return nullptr;
			}
			case 'f': {
				if (*++s == 'a') {
					if (*++s == 'l') {
						if (*++s == 's') {
							if (*++s == 'e') {
								if (proxy.onBoolean(false))
									return nullptr;
								return ++s;
							}
						}
					}
				}
				proxy.onError(s);
				return nullptr;
			}
			case 't': {
				if (*++s == 'r') {
					if (*++s == 'u') {
						if (*++s == 'e') {
							if (proxy.onBoolean(true))
								return nullptr;
							return ++s;
						}
					}
				}
				proxy.onError(s);
				return nullptr;
			}
			case 'n': {
				if (*++s == 'u') {
					if (*++s == 'l') {
						if (*++s == 'l') {
							if (proxy.onNull())
								return nullptr;
							return ++s;
						}
					}
				}
				proxy.onError(s);
				return nullptr;
			}
			case '{': {
				if (proxy.intoDict())
					return nullptr;
				__skip(++s);
				if (*s == '}') {
					if (proxy.exitDict())
						return nullptr;
					return ++s;
				}
				while (true) {
					if (*s != '"') {
						proxy.onError(s);
						return nullptr;
					}
					for (const char *p = ++s; ; ++p) {
						if (__builtin_expect(*p == '"', 0)) {
							if (proxy.onKey(s, p))
								return nullptr;
							s = ++p;
							break;
						}
						if (*p == '\\')
							++p;
						if (__builtin_expect(*p == '\0', 0)) {
							proxy.onError(p);
							return nullptr;
						}
					}
					__skip(s);
					if (*s == ':') {
						if (proxy.onPair())
							return nullptr;
            __skip(++s);
						s = parse(s, proxy);
						if (!s)
							return s;
            __skip(s);
						if (*s == ',') {
							if (proxy.onMore())
								return nullptr;
              __skip(++s);
							continue;
						}
						if (*s == '}')
							break;
					}
					proxy.onError(s);
					return nullptr;
				}
				if (proxy.exitDict())
					return nullptr;
				return ++s;
			}
			case '[': {
				if (proxy.intoList())
					return nullptr;
        __skip(++s);
				if (*s == ']') {
					if (proxy.exitList())
						return nullptr;
					return ++s;
				}
				while (true) {
					s = parse(s, proxy);
					if (!s)
						return s;
          __skip(s);
					if (*s == ',') {
						if (proxy.onMore())
							return nullptr;
            __skip(++s);
						continue;
					}
					if (*s == ']')
						break;
					proxy.onError(s);
					return nullptr;
				}
				if (proxy.exitList())
					return nullptr;
				return ++s;
			}
			default: {
				proxy.onError(s);
				return nullptr;
			}
		}
	} // const char *parse (const char *s, Proxy &proxy)
		
		
} // namespace jlib::json
} // namespace jlib